iOS7 之前
OC 调用 JS
在iOS7
以前,在OC
中调用JavaScript
的方式只有一种,就是通过UIWebView
对象的stringByEvaluatingJavaScriptFromString:
方法,可以实现的交互很少,还需要JS提供相应的方法,实现功能,常用代码如下:
1 | NSString *title =[webView stringByEvaluatingJavaScriptFromString:@"document.title"]; //得到网页标题 |
JS 调用 OC
通过UIWebView
的重定向,通过在UIWebView
的代理函数webView:shouldStartLoadWithRequest:navigationType:
来监听URL
的变化,这个函数会在webview
加载URL
时回调,我们可以return YES
来让webview
继续加载内容,return NO
来停止继续加载新的内容。要约定好几个虚假的URL(比如tool://goToHomePage
),我们拿到定义好的URL
之后做对应的Objective-C
函数调用。
比如我们在JavaScript
文件里添加点击响应的函数
function clickButton() {
window.location = "tool://goToHomePage"
}
然后在webview
的代理函数中监听,如果是事先定义好的虚假URL
,就进行对应的处理
1 | -(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { |
可以看出这种做法非常笨重,效率低下,无法实现混合开发。接下来要说到这篇博客的重点,利用JavaScriptCore
实现OC
和JS
之间的深度交互。
JavaScriptCore
类概述
JavaScriptCore
头文件中包括以下几类:
#import "JSContext.h"
#import "JSValue.h"
#import "JSManagedValue.h"
#import "JSVirtualMachine.h"
#import "JSExport.h"
JSContext
JavaScript
的运行环境,你需要用JSContext
来执行JavaScript
代码。
JSValue
JavaScript
实体,JSValue
可以表示很多JavaScript
原始类型例如boolean, integers, doubles,甚至包括对象,每个JSValue
都是强引用一个JSContext
。
JSManagedValue
内存管理辅助对象,用来避免循环引用。本质上属于JSValue
实例。
JSVirtualMachine
JS
运行的虚拟机,有独立的堆空间和垃圾回收机制。
JSExport
协议,JS
对象直接调用OC
对象的属性及方法必须实现的协议。
OC 调用 JS
先加入JavaScriptCore
的头文件。
#import <JavaScriptCore/JavaScriptCore.h>
实例代码,OC
调用JS
实现 两个数字相加
1 | // 初始化对象 |
JSValue
包括一系列方法用于访问其可能的值以保证有正确的 Foundation
类型,包括:
JavaScript Type | JSValue method | Objective-C Type |
---|---|---|
string | toString | NSString |
number | toNumber toDouble toInt32 toUInt32 |
NSNumber double int32_t uint32_t |
Date | toDate | NSDate |
Array | toArray | NSArray |
Object | toDictionary | NSDictionary |
Object | toObject toObjectOfClass: |
JS 调用 OC
JS调用OC有两个方法:Block
和JSExport
。
Block
稍微复杂的运算给出一个数字,从1加到当前数字,将此函数在OC中实现,在JS中调用,代码如下:
1 | // 初始化对象 |
我们可以得到JS执行上下文,利用JS语句得到相对应的DOM对象,运行OC中自定义的方法,完美。
JSExport
JSExport
是一个协议,可以让原生类的属性或方法转化为JavaScript
的属性或方法。需要我们自定义一个协议继承JSExport
,然后我们在签署我们自定义的协议。代码如下:
@protocol MyExport <JSExport>
@end
@interface ViewController : UIViewController <MyExport>
@end
例如有如下JavaScript
代码
1 | function Person(name, age) { |
可以在Objective-C
中把Person
对象传递给addPerson
函数
1 | Person *aPerson = [[Person alloc] init]; |
异常处理
1 | context.exceptionHandler = ^(JSContext *context, JSValue *exception) { |
内存管理
Objective-C
的内存管理机制是引用技术,JavaScript
的内存管理机制是垃圾回收。由于每个JSValue
都是强引用一个JSContext
,可能会存在循环引用的情况。理解不深,不便多言。